package org.apache.ocean.main.client;

import java.net.URL;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;

import org.apache.ocean.HighlightResponse.DocumentHighlight;
import org.apache.ocean.main.IndexDocuments;
import org.apache.ocean.main.IndexSchema;
import org.apache.ocean.main.IndexDocuments.IndexDocument;
import org.apache.ocean.main.http.HttpParameters;
import org.apache.ocean.main.http.HttpTimeout;
import org.apache.ocean.main.util.CElement;
import org.apache.ocean.main.util.Constants;
import org.apache.ocean.main.util.NamedList;
import org.apache.ocean.main.util.XMLUtil;
import org.jdom.Element;

public class SearchClient {
  
	public static Results search(URL url, Search search, HttpTimeout httpTimeout) throws Exception {
		HttpParameters httpParameters = new HttpParameters();
		search.transferTo(httpParameters);
		OceanXML response = OceanXML.request(url, httpParameters, httpTimeout);
		int numFound = response.getHeaderInt("numfound");
		IndexSchema.Category category = search.getIndexSchemaCategory();
		IndexDocuments indexDocuments = new IndexDocuments();
		for (Element element : response.getBodyChildren()) {
			IndexDocument indexDocument = new IndexDocument(element, category);
			indexDocuments.add(indexDocument);
		}
		return new Results(indexDocuments, numFound);
	}
	
	public static class Results {
		private IndexDocuments indexDocuments;
		private int numFound;
		
		public Results(IndexDocuments indexDocuments, int numFound) {
			this.indexDocuments = indexDocuments;
			this.numFound = numFound;
		}
	}
	
	public static class HighlightResult extends NamedList<String,DocumentHighlight> {
		
		public HighlightResult(Element element) {
			
		}
		
		public static class DocumentHighlight extends NamedList<String,String> {
			private String id;
			
			public DocumentHighlight(Element root) {
				id = root.getAttributeValue(Constants.ID); 
				for (Element element : XMLUtil.getChildren(root)) {
					String field = element.getName();
					add(field, element.getText());
				}
			}

			public Element toElement() {
				Element documentHighlightElement = new Element("documentHighlight");
				documentHighlightElement.setAttribute("id", id);
				for (NamedList.Entry<String, String> entry : getEntries()) {
					if (entry.value == null)
						continue;
					XMLUtil.createTextElement(entry.key, entry.value, documentHighlightElement);
				}
				return documentHighlightElement;
			}
		}
	}
	
	public static class Highlight {
		private String fields;
		private Integer snippets;
		private Integer fragmentSize;
		private String formatter;
		private String simplePre;
		private String simplePost;
		
		public Highlight(Integer snippets, Integer fragmentSize, String formatter, String simplePre, String simplePost) {
			this.snippets = snippets;
			this.fragmentSize = fragmentSize;
			this.formatter = formatter;
			this.simplePre = simplePre;
			this.simplePost = simplePost;
		}

		public void transferTo(HttpParameters parameters) {
			parameters.add("highlightfields", fields);
			parameters.add("snippets", snippets);
			parameters.add("fragmentsize", fragmentSize);
			parameters.add("formatter", formatter);
			parameters.add("formatter", formatter);
			parameters.add("simplePost", simplePost);
		}
	}
	
	public static class Search {
		private int start;
		private int num;
		private String query;
		private String fields;
		private List<String> filters;
		private IndexSchema.Category indexSchemaCategory;
		private Highlight highlight;
		
		public Search(int start, int num, String query, String fields, List<String> filters, IndexSchema.Category indexSchemaCategory) {
			this.start = start;
			this.num = num;
			this.query = query;
			this.fields = fields;
			this.filters = filters;
			this.indexSchemaCategory = indexSchemaCategory;
		}
    
		public int getStart() {
			return start;
		}

		public int getNum() {
			return num;
		}

		public String getQuery() {
			return query;
		}

		public String getFields() {
			return fields;
		}

		public List<String> getFilters() {
			return filters;
		}

		public IndexSchema.Category getIndexSchemaCategory() {
			return indexSchemaCategory;
		}

		public void transferTo(HttpParameters parameters) {
			parameters.add("num", num);
			parameters.add("start", start);
			parameters.add("q", query);
			parameters.add("fields", fields);
			for (String filter : filters) {
				parameters.add("filter", filter);
			}
			parameters.add("schemaversion", indexSchemaCategory.getIndexSchema().getVersion());
			if (highlight != null) {
				highlight.transferTo(parameters);
			}
		}
	}
	
	public static void writeBatch(URL url, String category, long schemaVersion, IndexDocuments indexDocuments, List<String> deletes, long timeout, HttpTimeout httpTimeout) throws Exception {
		Batch batch = new Batch(indexDocuments, deletes);
		OceanXML request = new OceanXML();
		request.addBody(batch);
		HttpParameters parameters = new HttpParameters();
		parameters.add("action", "writebatch");
		parameters.add("schemaversion", schemaVersion);
		parameters.add("category", category);
		parameters.add("timeout", timeout);
		OceanXML.request(new URL(url+"?"+parameters.toEncodedQueryString()), request, httpTimeout);
	}
	
	public static class Batch implements CElement {
		private IndexDocuments indexDocuments;
		private List<String> deletes;
		
		public Batch(Element element, IndexSchema.Category indexSchemaCategory) throws ParseException {
			deletes = new ArrayList<String>();
			indexDocuments = new IndexDocuments();
			for (Element deleteElement : XMLUtil.getChildren("delete", element)) {
				deletes.add(deleteElement.getText());
			}
			for (Element documentElement : XMLUtil.getChildren("document", element)) {
				indexDocuments.add(new IndexDocument(documentElement, indexSchemaCategory));
			}
		}
		
		public Batch(IndexDocuments indexDocuments, List<String> deletes) {
			this.indexDocuments = indexDocuments;
			this.deletes = deletes;
		}
		
		public List<String> getDeletes() {
			return deletes;
		}
		
		public IndexDocuments getDocuments() {
			return indexDocuments;
		}
		
		public Element toElement() {
			Element element = new Element("batch");
			for (String delete : deletes) {
				XMLUtil.createTextElement("delete", delete, element);
			}
			for (IndexDocument document : indexDocuments) {
			  element.addContent(document.toElement());
			}
			return element;
		}
	}
}
